Skip to main content

Complete Frontend System Design Guide

Table of Contents​

  1. Prerequisites

  2. Requirements Exploration

  3. High-Level Architecture

  4. State Management

  5. Data Modeling

  6. API & Interface Design

  7. Performance Optimization

  8. Scalability & Maintainability

  9. Security

  10. Advanced Topics

  11. RADIO Framework Example


1. Prerequisites​

1.1 JavaScript Internals​

Event Loop

  • Call stack, task queue, microtask queue
  • Promise resolution order
  • setTimeout vs Promise.then

Closures

  • Lexical scoping
  • Module pattern
  • Memory implications

Prototypal Inheritance

  • Prototype chain
  • __proto__ vs prototype
  • Constructor functions vs classes

Async Patterns

  • Callbacks, Promises, async/await
  • Error handling in async code
  • Race conditions

Memory Leaks

  • Event listener cleanup
  • Closures holding references
  • Detached DOM nodes

1.2 Browser Fundamentals​

Rendering Pipeline

  1. Parse HTML β†’ DOM tree
  2. Parse CSS β†’ CSSOM tree
  3. Combine β†’ Render tree
  4. Layout (reflow)
  5. Paint
  6. Composite layers

Reflow vs Repaint

  • Reflow: Geometry changes (expensive)
  • Repaint: Visual changes (cheaper)
  • Composite: GPU-accelerated (cheapest)

Critical Rendering Path

  • Blocking vs non-blocking resources
  • Async vs defer scripts
  • CSS render blocking

1.3 React Internals​

Reconciliation

  • Virtual DOM diffing
  • Keys importance
  • Fiber architecture

Hooks Lifecycle

  • Mounting, updating, unmounting phases
  • Effect cleanup functions
  • Dependency arrays

Controlled vs Uncontrolled

  • Single source of truth
  • Performance implications
  • Use cases for each

2. Requirements Exploration​

2.1 Functional Requirements​

Define what the system must do:

  • User actions (CRUD operations)
  • Business logic
  • Features and capabilities
  • User workflows

2.2 Non-Functional Requirements​

Define how the system should perform:

  • Performance (load time, FPS)
  • Scalability (user count, data volume)
  • Availability (uptime, offline support)
  • Security (auth, data protection)
  • Accessibility (WCAG compliance)
  • Browser support
  • Mobile responsiveness

2.3 Scoping Questions Framework​

Users & Context

  • Who are the primary users?
  • What devices/browsers?
  • Geographic distribution?
  • Network conditions?

Scale & Performance

  • Expected user count?
  • Peak traffic patterns?
  • Data volume?
  • Performance targets (TTI < 3s, FCP < 1.5s)?

Features & Constraints

  • Core features vs nice-to-have?
  • Real-time requirements?
  • Offline support needed?
  • Integration points?

Business & Technical

  • Time to market?
  • Team size/structure?
  • Legacy system constraints?
  • Budget considerations?

3. High-Level Architecture​

3.1 Monolith SPA​

When to use:

  • Small to medium applications
  • Single team
  • Unified user experience

Structure:

src/
β”œβ”€β”€ features/
β”‚ β”œβ”€β”€ auth/
β”‚ β”œβ”€β”€ dashboard/
β”‚ └── settings/
β”œβ”€β”€ shared/
β”‚ β”œβ”€β”€ components/
β”‚ β”œβ”€β”€ hooks/
β”‚ └── utils/
└── app/

Pros:

  • Simple deployment
  • Easier state sharing
  • Lower initial complexity

Cons:

  • Harder to scale teams
  • All-or-nothing deploys
  • Monolithic bundle

3.2 Modular Monolith​

When to use:

  • Growing applications
  • Multiple feature teams
  • Need clear boundaries

Structure:

packages/
β”œβ”€β”€ auth-module/
β”œβ”€β”€ orders-module/
β”œβ”€β”€ payments-module/
└── shared-ui/

Pros:

  • Clear module boundaries
  • Incremental migration path
  • Still simpler than micro frontends

Cons:

  • Requires discipline
  • Shared dependencies management

3.3 Micro Frontends​

Build-time Integration (Nx, Turborepo)

  • Compile-time composition
  • Shared build pipeline
  • Type safety across modules

Runtime Integration (Module Federation)

  • Independent deployments
  • Dynamic loading
  • Version independence

When to use:

  • Large organizations
  • Multiple autonomous teams
  • Different release cycles

Architecture:

Shell App (Host)
β”œβ”€β”€ Header
β”œβ”€β”€ Navigation
β”œβ”€β”€ Router
└── Remote Apps
β”œβ”€β”€ Orders (@orders/latest)
β”œβ”€β”€ Payments (@payments/v2.1)
└── Profile (@profile/latest)

Pros:

  • Team autonomy
  • Independent deployments
  • Technology flexibility

Cons:

  • Operational complexity
  • Shared state challenges
  • Performance overhead

3.4 Rendering Strategies​

CSR (Client-Side Rendering)

  • Best for: Highly interactive apps, authenticated content
  • Pros: Rich interactions, API-driven
  • Cons: SEO challenges, slower FCP

SSR (Server-Side Rendering)

  • Best for: Content sites, e-commerce
  • Pros: Fast FCP, SEO-friendly
  • Cons: Server load, full page hydration

SSG (Static Site Generation)

  • Best for: Blogs, documentation, marketing
  • Pros: Fast, cacheable, cheap hosting
  • Cons: Build time, stale data

Islands Architecture

  • Best for: Content-heavy with interactive widgets
  • Pros: Minimal JS, fast, progressive enhancement
  • Cons: Framework limitations

Streaming SSR

  • Best for: Large pages with prioritized content
  • Pros: Progressive rendering, TTFB
  • Cons: Complexity, error handling

4. State Management​

4.1 State Types​

UI State

  • Modal open/closed
  • Form input values
  • Selected tabs
  • Theme preference

Server Cache

  • API response data
  • Temporary/stale data
  • Background sync

URL State

  • Search filters
  • Pagination
  • Sort order
  • Shareable state

Local Persistent State

  • User preferences
  • Draft content
  • Cart items (before checkout)

4.2 State Management Solutions​

Local State (useState)

  • Component-specific
  • No sharing needed
  • Simple, performant

Context API

  • Theme, i18n, auth
  • Infrequent updates
  • Avoid frequent re-renders

Redux / Zustand / Jotai

  • Complex global state
  • Time-travel debugging
  • Predictable updates

React Query / SWR

  • Server state ONLY
  • Automatic caching
  • Background refetching
  • Optimistic updates

URL State (useSearchParams)

  • Filters, pagination
  • Shareable links
  • Browser history

4.3 Trade-offs Analysis​

SolutionUse WhenAvoid When
useStateComponent-only dataSharing across components
ContextInfrequent updatesFrequent state changes
ReduxComplex state logicSimple CRUD apps
React QueryServer dataLocal UI state
URLShareable stateSensitive data

Anti-patterns:

  • Global state for everything
  • Context for frequently changing data
  • Redux for server data
  • localStorage for session data

5. Data Modeling​

5.1 Normalization Strategies​

Normalized (Relational)

{
users: {
'u1': { id: 'u1', name: 'Alice' },
'u2': { id: 'u2', name: 'Bob' }
},
posts: {
'p1': { id: 'p1', title: 'Hello', authorId: 'u1' },
'p2': { id: 'p2', title: 'World', authorId: 'u1' }
}
}

Pros:

  • No duplication
  • Easy updates
  • Consistent data

Cons:

  • Complex queries
  • Denormalization for UI

Denormalized (Document)

[
{
id: 'p1',
title: 'Hello',
author: { id: 'u1', name: 'Alice' }
},
{
id: 'p2',
title: 'World',
author: { id: 'u1', name: 'Alice' }
}
]

Pros:

  • Fast reads
  • Simple queries
  • UI-friendly

Cons:

  • Data duplication
  • Update complexity

Hybrid Approach:

  • Store normalized in cache
  • Denormalize for components
  • Use selectors/resolvers

5.2 Pagination Patterns​

Offset-based

GET /posts?offset=20&limit=10
  • Simple implementation
  • Good for small datasets
  • Issues: Inconsistent with real-time updates

Cursor-based

GET /posts?cursor=abc123&limit=10
Response: { data: [...], nextCursor: 'def456' }
  • Consistent pagination
  • Real-time friendly
  • Required for infinite scroll

Infinite Scroll vs Load More

  • Infinite: Social feeds, discovery
  • Load More: Search results, e-commerce

Virtualization

  • For large lists (1000+ items)
  • Libraries: react-window, react-virtualized
  • Render only visible rows

5.3 Caching Strategies​

Time-based Invalidation

staleTime: 5 * 60 * 1000 // 5 minutes

Tag-based Invalidation

invalidateQueries(['posts'])

Optimistic Updates

onMutate: async (newPost) => {
await queryClient.cancelQueries(['posts'])
const previous = queryClient.getQueryData(['posts'])
queryClient.setQueryData(['posts'], old => [...old, newPost])
return { previous }
}

6. API & Interface Design​

6.1 REST vs GraphQL​

REST

  • Simple, cacheable
  • Over-fetching/under-fetching
  • Multiple endpoints
  • Good for: Simple CRUD, public APIs

GraphQL

  • Single endpoint
  • Client-specified queries
  • No over-fetching
  • Good for: Complex data requirements, mobile apps

Best Practice:

  • Use REST by default
  • GraphQL for complex data needs
  • Consider BFF pattern

6.2 Error Handling​

HTTP Status Codes

200: Success
400: Bad Request (client error)
401: Unauthorized
403: Forbidden
404: Not Found
500: Server Error
503: Service Unavailable

Error Response Contract

{
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid email format',
field: 'email',
details: { ... }
}
}

Client-side Handling

try {
const data = await fetchAPI()
} catch (error) {
if (error.status === 401) {
// Redirect to login
} else if (error.status >= 500) {
// Show retry option
} else {
// Show error message
}
}

6.3 Backend For Frontend (BFF)​

Purpose:

  • Aggregate multiple APIs
  • Transform data for UI
  • Handle auth/authorization
  • Reduce client complexity

Example:

Mobile App β†’ Mobile BFF β†’ Microservices
Web App β†’ Web BFF β†’ Microservices

Benefits:

  • UI-optimized responses
  • Reduced network calls
  • Platform-specific logic
  • Better performance

7. Performance Optimization​

7.1 Loading Performance​

Code Splitting

// Route-based
const Orders = lazy(() => import('./Orders'))

// Component-based
const HeavyChart = lazy(() => import('./HeavyChart'))

Tree Shaking

  • Use ES modules
  • Avoid import *
  • Side-effects annotation

Image Optimization

<img
src="image.jpg"
loading="lazy"
srcset="small.jpg 480w, large.jpg 1080w"
sizes="(max-width: 600px) 480px, 1080px"
/>

Prefetch/Preload

<link rel="preload" href="critical.css" as="style">
<link rel="prefetch" href="next-page.js">
<link rel="dns-prefetch" href="https://api.example.com">

Web Workers

  • Heavy computations
  • Data processing
  • Image manipulation

7.2 Runtime Performance​

Memoization

const expensiveValue = useMemo(() =>
computeExpensive(a, b), [a, b]
)

const handleClick = useCallback(() => {
doSomething(id)
}, [id])

Virtualization

<VirtualList
height={600}
itemCount={1000}
itemSize={50}
width="100%"
>
{Row}
</VirtualList>

Debouncing/Throttling

const debouncedSearch = debounce(search, 300)
const throttledScroll = throttle(handleScroll, 100)

Avoid Re-renders

  • React.memo for pure components
  • Proper dependency arrays
  • State colocation
  • Context splitting

7.3 Key Metrics​

Loading Metrics

  • FCP (First Contentful Paint): < 1.8s
  • LCP (Largest Contentful Paint): < 2.5s
  • TTI (Time to Interactive): < 3.5s
  • TBT (Total Blocking Time): < 300ms

Runtime Metrics

  • FPS: 60fps (16.6ms per frame)
  • CLS (Cumulative Layout Shift): < 0.1
  • FID (First Input Delay): < 100ms

Bundle Metrics

  • Initial bundle: < 200KB (gzipped)
  • Main thread work: < 50ms chunks
  • JavaScript execution: < 2s

Measurement Tools

  • Lighthouse
  • WebPageTest
  • Chrome DevTools Performance
  • Real User Monitoring (RUM)

8. Scalability & Maintainability​

8.1 Code Organization​

Feature-based Structure

src/
β”œβ”€β”€ features/
β”‚ β”œβ”€β”€ auth/
β”‚ β”‚ β”œβ”€β”€ components/
β”‚ β”‚ β”œβ”€β”€ hooks/
β”‚ β”‚ β”œβ”€β”€ api/
β”‚ β”‚ └── index.ts
β”‚ └── orders/
└── shared/

Domain-driven Structure

src/
β”œβ”€β”€ domains/
β”‚ β”œβ”€β”€ user/
β”‚ β”œβ”€β”€ product/
β”‚ └── order/
β”œβ”€β”€ infrastructure/
└── application/

Best Practices:

  • Colocation of related code
  • Clear public interfaces
  • Dependency rules (inner β†’ outer)
  • Minimal coupling

8.2 Design Systems​

Component Library

  • Primitive components (Button, Input)
  • Composite components (Form, Card)
  • Layout components (Grid, Stack)
  • Consistent theming

Storybook Benefits

  • Component documentation
  • Visual testing
  • Isolated development
  • Design-dev collaboration

Versioning Strategy

  • Semantic versioning
  • Changelog maintenance
  • Deprecation notices
  • Migration guides

8.3 Monorepo Strategies​

Tools: Nx, Turborepo, Lerna

Structure:

packages/
β”œβ”€β”€ ui-components/
β”œβ”€β”€ utils/
β”œβ”€β”€ app-admin/
β”œβ”€β”€ app-customer/
└── shared-types/

Benefits:

  • Code sharing
  • Atomic changes
  • Unified tooling
  • Consistent versioning

Challenges:

  • Build complexity
  • CI/CD setup
  • Dependency management
  • Access control

9. Security​

9.1 Common Vulnerabilities​

XSS (Cross-Site Scripting)

  • Sanitize user input
  • Use textContent vs innerHTML
  • CSP headers
  • React automatically escapes

CSRF (Cross-Site Request Forgery)

  • CSRF tokens
  • SameSite cookies
  • Origin verification

Clickjacking

  • X-Frame-Options header
  • CSP frame-ancestors

Open Redirects

  • Validate redirect URLs
  • Whitelist domains

9.2 Authentication & Authorization​

Token Storage

StorageProsConsUse Case
httpOnly CookieXSS-safeCSRF riskSession tokens
localStorageEasy accessXSS riskNon-sensitive data
MemoryMost secureLost on refreshShort-lived tokens

Best Practice:

  • Access token in memory (short-lived)
  • Refresh token in httpOnly cookie
  • Rotate tokens
  • Implement token expiry

JWT Considerations

  • Don't store sensitive data
  • Validate on server
  • Short expiration
  • Signature verification

9.3 Secure Communication​

CORS

Access-Control-Allow-Origin: https://trusted.com
Access-Control-Allow-Credentials: true

CSP (Content Security Policy)

Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-xyz';
img-src 'self' https://cdn.example.com;

Iframe Communication

// Only accept from trusted origin
window.addEventListener('message', (event) => {
if (event.origin !== 'https://trusted.com') return
// Handle message
})

10. Advanced Topics​

10.1 Offline-First Architecture​

Service Workers

  • Cache API responses
  • Offline page fallback
  • Background sync

IndexedDB

  • Local database
  • Large data storage
  • Complex queries

Sync Strategy

// Save locally first
saveToLocal(data)

// Sync when online
if (navigator.onLine) {
syncToServer(data)
}

Conflict Resolution

  • Last-write-wins
  • Operational transforms
  • CRDTs

10.2 Real-Time Features​

WebSockets

const ws = new WebSocket('wss://api.example.com')
ws.onmessage = (event) => {
updateUI(JSON.parse(event.data))
}

Server-Sent Events (SSE)

const source = new EventSource('/events')
source.onmessage = (event) => {
console.log(event.data)
}

When to use:

  • WebSockets: Bidirectional, gaming, chat
  • SSE: Unidirectional, notifications, feeds
  • Polling: Legacy, simple updates

10.3 Feature Management​

Feature Flags

if (featureFlags.isEnabled('new-checkout')) {
return <NewCheckout />
}
return <OldCheckout />

A/B Testing

const variant = getExperimentVariant('checkout-redesign')
return variant === 'A' ? <VariantA /> : <VariantB />

Progressive Rollout

  • 5% β†’ 25% β†’ 50% β†’ 100%
  • Monitor metrics at each stage
  • Quick rollback capability

10.4 Observability​

Error Boundaries

<ErrorBoundary fallback={<ErrorPage />}>
<App />
</ErrorBoundary>

Logging

  • Client-side errors
  • User actions
  • Performance metrics
  • API failures

Monitoring Tools

  • Sentry (errors)
  • LogRocket (session replay)
  • Datadog (APM)
  • Google Analytics (user behavior)

Key Metrics to Track

  • Error rate
  • Load time (P50, P95, P99)
  • User flows completion
  • API response times

11. RADIO Framework Example​

Let's design an E-commerce Product Search & Filters page using the RADIO framework.


Requirements Clarification​

Functional Requirements​

  1. Search

    • Full-text product search
    • Auto-complete suggestions
    • Search history
  2. Filters

    • Category, price range, brand, rating
    • Multiple filter selection
    • Clear filters option
  3. Results

    • Product grid/list view
    • Pagination or infinite scroll
    • Sort options (price, rating, relevance)
  4. Product Card

    • Image, title, price, rating
    • Quick view modal
    • Add to cart

Non-Functional Requirements​

  1. Performance

    • Search results < 500ms
    • FCP < 1.5s
    • Smooth scrolling (60fps)
  2. Scale

    • 1M+ products in catalog
    • 10k concurrent users
    • Real-time inventory updates
  3. UX

    • Responsive (mobile-first)
    • Offline search history
    • Accessibility (keyboard nav, screen readers)
  4. SEO

    • Server-side rendering
    • URL-based state
    • Open Graph tags

Scoping Questions​

  • Search algorithm (Elasticsearch? Algolia?)
  • Real-time inventory? (Yes)
  • Personalized results? (Phase 2)
  • Multi-language? (Yes - 3 languages)
  • Analytics tracking? (Yes - search terms, clicks)

Architecture / High-Level Design​

Rendering Strategy​

SSR + Client-side hydration

  • Initial render server-side (SEO, FCP)
  • Hydrate for interactivity
  • Client-side updates for filters/search

Component Architecture​

SearchPage
β”œβ”€β”€ SearchBar
β”‚ β”œβ”€β”€ AutoComplete
β”‚ └── SearchHistory
β”œβ”€β”€ Filters (Sidebar)
β”‚ β”œβ”€β”€ CategoryFilter
β”‚ β”œβ”€β”€ PriceRangeFilter
β”‚ β”œβ”€β”€ BrandFilter
β”‚ └── RatingFilter
β”œβ”€β”€ ResultsHeader
β”‚ β”œβ”€β”€ SortDropdown
β”‚ β”œβ”€β”€ ViewToggle (Grid/List)
β”‚ └── ResultsCount
└── ProductGrid
β”œβ”€β”€ ProductCard (Γ— N)
└── LoadMoreButton / InfiniteScroll

State Management​

// URL State (shareable, SEO)
?q=laptop&category=electronics&minPrice=500&maxPrice=1500&sort=price_asc&page=2

// Server State (React Query)
{
products: [...],
facets: { categories: [...], brands: [...] },
totalResults: 1234
}

// UI State (useState)
{
viewMode: 'grid',
selectedProduct: null,
isFilterPanelOpen: false
}

API Design​

GET /api/search?
q=laptop
&filters=category:electronics,brand:apple,price:500-1500
&sort=price_asc
&page=2
&limit=20

Response:
{
results: [
{ id, title, price, image, rating, inStock, ... }
],
facets: {
categories: [{ name, count }],
brands: [{ name, count }],
priceRanges: [{ min, max, count }]
},
pagination: {
total: 1234,
page: 2,
limit: 20,
hasNext: true
}
}

Data Model​

Client-side Data Structure​

// Normalized (for updates)
{
products: {
'p1': { id: 'p1', title: 'MacBook Pro', price: 1999, ... },
'p2': { id: 'p2', title: 'Dell XPS', price: 1299, ... }
},
searchResults: {
'laptop-electronics-500-1500-page2': {
productIds: ['p1', 'p2', ...],
facets: { ... },
pagination: { ... },
timestamp: 1234567890
}
}
}

// Selectors resolve relationships
const getSearchResults = (state, cacheKey) => {
const result = state.searchResults[cacheKey]
return result.productIds.map(id => state.products[id])
}

Cache Strategy​

useQuery(
['search', searchParams],
() => fetchSearchResults(searchParams),
{
staleTime: 60000, // 1 minute
cacheTime: 300000, // 5 minutes
keepPreviousData: true // Avoid loading state on filter change
}
)

Interface Definition (API)​

Search API​

interface SearchParams {
query: string
filters: {
categories?: string[]
brands?: string[]
priceRange?: { min: number, max: number }
minRating?: number
}
sort: 'relevance' | 'price_asc' | 'price_desc' | 'rating'
pagination: {
page: number
limit: number
}
}

interface SearchResponse {
results: Product[]
facets: Facets
pagination: PaginationMeta
}

WebSocket (Real-time Inventory)​

ws.on('inventory-update', (data) => {
// { productId: 'p1', inStock: false }
queryClient.setQueryData(['product', data.productId], (old) => ({
...old,
inStock: data.inStock
}))
})

Optimizations & Deep Dive​

1. Search Performance​

Debounced Search

const debouncedSearch = useMemo(
() => debounce((query) => {
fetchSearchResults(query)
}, 300),
[]
)

Autocomplete

// Separate lightweight API
GET /api/autocomplete?q=lap
Response: ['laptop', 'laptop bag', 'laptop stand']

// Cache aggressively
staleTime: Infinity

Search History (Offline)

// Store in localStorage
const saveSearchHistory = (query) => {
const history = JSON.parse(localStorage.getItem('searchHistory') || '[]')
history.unshift(query)
localStorage.setItem('searchHistory', JSON.stringify(history.slice(0, 10)))
}

2. Filter Performance​

URL as State

const [searchParams, setSearchParams] = useSearchParams()

const updateFilter = (key, value) => {
const newParams = new URLSearchParams(searchParams)
newParams.set(key, value)
setSearchParams(newParams) // Updates URL + triggers search
}

Optimistic Filter Updates

const applyFilter = (filter) => {
// Update UI immediately
setLocalFilters(prev => [...prev, filter])

// Fetch in background
queryClient.prefetchQuery(['search', newParams])
}

3. Product Grid Performance​

Virtualization (for 1000+ products)

import { FixedSizeGrid } from 'react-window'

<FixedSizeGrid
columnCount={4}
columnWidth={250}
height={600}
rowCount={Math.ceil(products.length / 4)}
rowHeight={350}
width={1000}
>
{ProductCard}
</FixedSizeGrid>

Image Optimization

<img
src={`${CDN_URL}/products/${id}/thumb.webp`}
srcset={`
${CDN_URL}/products/${id}/thumb.webp 300w,
${CDN_URL}/products/${id}/medium.webp 600w
`}
loading="lazy"
alt={title}
/>

Intersection Observer (Lazy Load)

const { ref, inView } = useInView({
triggerOnce: true,
threshold: 0.1
})

return (
<div ref={ref}>
{inView ? <ProductCard {...product} /> : <Skeleton />}
</div>
)

4. Infinite Scroll vs Pagination​

Infinite Scroll (chosen)

const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage
} = useInfiniteQuery(
['search', searchParams],
({ pageParam = 1 }) => fetchSearchResults({ ...searchParams, page: pageParam }),
{
getNextPageParam: (lastPage) =>
lastPage.pagination.hasNext ? lastPage.pagination.page + 1 : undefined
}
)

// Trigger on scroll
useEffect(() => {
const handleScroll = () => {
if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 500) {
if (hasNextPage && !isFetchingNextPage) {
fetchNextPage()
}
}
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll)
}, [hasNextPage, isFetchingNextPage, fetchNextPage])

Why Infinite Scroll?

  • Better mobile experience
  • Encourages discovery
  • Seamless browsing

Trade-offs:

  • Back button behavior (solved with URL state)
  • Footer accessibility (use "Load More" button at end)
  • Memory usage (virtualization helps)

5. Code Splitting​

Route-based Splitting

const SearchPage = lazy(() => import('./SearchPage'))
const ProductDetail = lazy(() => import('./ProductDetail'))
const Checkout = lazy(() => import('./Checkout'))

<Suspense fallback={<PageLoader />}>
<Routes>
<Route path="/search" element={<SearchPage />} />
<Route path="/product/:id" element={<ProductDetail />} />
<Route path="/checkout" element={<Checkout />} />
</Routes>
</Suspense>

Component-based Splitting

// Heavy chart library - only load when needed
const PriceHistoryChart = lazy(() => import('./PriceHistoryChart'))

{showPriceHistory && (
<Suspense fallback={<ChartSkeleton />}>
<PriceHistoryChart data={priceData} />
</Suspense>
)}

Prefetching on Hover

const prefetchProductDetail = (productId) => {
queryClient.prefetchQuery(['product', productId], () =>
fetchProductDetail(productId)
)
}

<Link
to={`/product/${id}`}
onMouseEnter={() => prefetchProductDetail(id)}
>
View Details
</Link>

6. Security Considerations​

XSS Prevention

// Sanitize search query in URL
const safeQuery = DOMPurify.sanitize(searchParams.get('q'))

// Use textContent for user-generated content
element.textContent = userInput // Safe
element.innerHTML = userInput // Dangerous

CSRF Protection

// Add CSRF token to cart/checkout actions
fetch('/api/cart/add', {
method: 'POST',
headers: {
'X-CSRF-Token': getCsrfToken(),
'Content-Type': 'application/json'
},
body: JSON.stringify({ productId, quantity })
})

Rate Limiting (Client-side)

// Prevent search spam
const rateLimiter = new RateLimiter({ maxRequests: 10, perSeconds: 60 })

const handleSearch = async (query) => {
if (!rateLimiter.allow()) {
showError('Too many searches. Please wait.')
return
}
await fetchSearchResults(query)
}

7. Accessibility​

Keyboard Navigation

// Filter checkboxes
<input
type="checkbox"
role="checkbox"
aria-checked={isChecked}
onKeyDown={(e) => e.key === 'Enter' && toggle()}
/>

// Product grid navigation
useEffect(() => {
const handleKeyNav = (e) => {
if (e.key === 'ArrowRight') focusNextProduct()
if (e.key === 'ArrowLeft') focusPrevProduct()
}
document.addEventListener('keydown', handleKeyNav)
return () => document.removeEventListener('keydown', handleKeyNav)
}, [])

Screen Reader Support

<div
role="status"
aria-live="polite"
aria-atomic="true"
>
{isLoading ? 'Loading products...' : `${totalResults} products found`}
</div>

<button
aria-label={`Add ${productName} to cart`}
aria-describedby={`price-${productId}`}
>
Add to Cart
</button>

8. Analytics & Monitoring​

Search Analytics

// Track search queries
analytics.track('search_performed', {
query: searchQuery,
filters: activeFilters,
resultsCount: totalResults,
timestamp: Date.now()
})

// Track zero-results searches (for improvements)
if (totalResults === 0) {
analytics.track('search_no_results', { query: searchQuery })
}

Performance Monitoring

// Measure search response time
const startTime = performance.now()
await fetchSearchResults(query)
const duration = performance.now() - startTime

if (duration > 1000) {
logger.warn('Slow search', { query, duration })
}

9. Error Handling​

Graceful Degradation

const SearchPage = () => {
const { data, error, isLoading } = useQuery(['search', params])

if (error) {
return (
<ErrorBoundary>
<ErrorState
title="Search temporarily unavailable"
action={<Button onClick={retry}>Try Again</Button>}
fallback={<RecentProducts />}
/>
</ErrorBoundary>
)
}

// ... rest of component
}

Network Resilience

// Retry with exponential backoff
useQuery(['search', params], fetchSearch, {
retry: 3,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000)
})

// Offline detection
useEffect(() => {
const handleOffline = () => {
showNotification('You are offline. Showing cached results.')
}
window.addEventListener('offline', handleOffline)
return () => window.removeEventListener('offline', handleOffline)
}, [])

Summary: E-commerce Search Design​

Key Decisions Made​

AspectDecisionRationale
RenderingSSR + HydrationSEO + Performance
State ManagementURL + React QueryShareable + Cache optimization
PaginationInfinite ScrollBetter mobile UX
Real-timeWebSocketLive inventory updates
PerformanceVirtualizationHandle large catalogs
Caching1-minute stale timeBalance freshness + performance

Performance Budget​

  • FCP: < 1.5s βœ“
  • Search Response: < 500ms βœ“
  • Smooth Scroll: 60fps βœ“
  • Initial Bundle: < 150KB βœ“

Scalability​

  • Handles 1M+ products via backend search
  • Supports 10k concurrent users via caching
  • Horizontal scaling of search API
  • CDN for static assets

12. Additional RADIO Examples​

Example 2: Instagram Feed​

Requirements​

Functional:

  • Infinite scroll feed
  • Like, comment, share
  • Image/video posts
  • Stories at top
  • Real-time likes/comments

Non-Functional:

  • Load 20 posts initially
  • < 2s FCP
  • Smooth 60fps scroll
  • Offline viewing of cached posts
  • 100M+ users

Architecture​

FeedPage
β”œβ”€β”€ StoriesBar (horizontal scroll)
β”œβ”€β”€ CreatePost
└── FeedList (infinite scroll)
└── PostCard
β”œβ”€β”€ PostHeader (avatar, username, menu)
β”œβ”€β”€ PostMedia (image/video/carousel)
β”œβ”€β”€ PostActions (like, comment, share, save)
β”œβ”€β”€ PostLikes
β”œβ”€β”€ PostCaption
└── PostComments (first 3)

State Management:

// Server State (React Query)
- feed: { posts, cursor }
- stories: { items }

// UI State
- activeStory: number
- expandedComments: Set<postId>
- videosPlaying: Set<postId>

// Real-time (WebSocket)
- likes count updates
- new comments stream

Data Model​

// Normalized Store
{
users: {
'u1': { id, username, avatar, ... }
},
posts: {
'p1': {
id: 'p1',
authorId: 'u1',
mediaUrls: ['img1.jpg'],
caption: '...',
likesCount: 1234,
createdAt: '...'
}
},
comments: {
'c1': { id, postId, authorId, text, createdAt }
},
feed: {
postIds: ['p1', 'p2', ...],
cursor: 'abc123'
}
}

Interface​

GET /api/feed?cursor=abc&limit=20
Response: {
posts: [...],
cursor: 'def456',
hasMore: true
}

POST /api/posts/:id/like
WebSocket: { type: 'like', postId: 'p1', count: 1235 }

GET /api/stories
Response: {
stories: [{ userId, items: [...], seenAt }]
}

Optimizations​

1. Virtual Scrolling

<VirtualScroller
itemSize={600} // avg post height
buffer={2} // render 2 extra posts
onEndReached={loadMore}
>
{posts.map(post => <PostCard key={post.id} {...post} />)}
</VirtualScroller>

2. Image Optimization

// Progressive loading
<img
src={thumbnailUrl} // 10KB placeholder
data-src={fullImageUrl}
onLoad={loadFullImage}
loading="lazy"
/>

// Responsive images
srcset="
small.jpg 480w,
medium.jpg 1080w,
large.jpg 1920w
"

3. Video Auto-play

// Only play videos in viewport
const { ref, inView } = useInView({ threshold: 0.5 })

useEffect(() => {
if (inView) {
videoRef.current?.play()
} else {
videoRef.current?.pause()
}
}, [inView])

4. Optimistic Updates

const likeMutation = useMutation(likePost, {
onMutate: async (postId) => {
await queryClient.cancelQueries(['post', postId])

const previous = queryClient.getQueryData(['post', postId])

queryClient.setQueryData(['post', postId], old => ({
...old,
isLiked: true,
likesCount: old.likesCount + 1
}))

return { previous }
},
onError: (err, postId, context) => {
queryClient.setQueryData(['post', postId], context.previous)
}
})

5. Offline Support

// Cache feed in IndexedDB
useEffect(() => {
if (posts.length > 0) {
db.posts.bulkPut(posts)
}
}, [posts])

// Show cached posts when offline
const { data: cachedPosts } = useQuery(
'cachedFeed',
() => db.posts.limit(20).toArray(),
{ enabled: !navigator.onLine }
)

Example 3: Google Docs (Collaborative Text Editor)​

Requirements​

Functional:

  • Real-time collaborative editing
  • Rich text formatting
  • Comments and suggestions
  • Version history
  • Auto-save
  • Offline editing

Non-Functional:

  • < 100ms typing latency
  • Support 50+ concurrent editors
  • Handle 100-page documents
  • Conflict-free sync
  • 99.9% uptime

Architecture​

EditorPage
β”œβ”€β”€ Toolbar
β”‚ β”œβ”€β”€ FormatButtons
β”‚ β”œβ”€β”€ UndoRedo
β”‚ └── ShareButton
β”œβ”€β”€ CollaboratorsCursor (multiple)
β”œβ”€β”€ EditorCanvas
β”‚ └── ContentEditable / Draft.js / Slate
β”œβ”€β”€ Sidebar
β”‚ β”œβ”€β”€ CommentThread
β”‚ └── SuggestionsList
└── AutoSaveIndicator

CRDT for Conflict Resolution:

// Use Yjs or Automerge for CRDT
const ydoc = new Y.Doc()
const ytext = ydoc.getText('content')

// Sync via WebSocket
const provider = new WebsocketProvider(
'wss://sync.example.com',
documentId,
ydoc
)

Data Model​

// Document Structure
{
id: 'doc123',
title: 'My Document',
content: Y.Text, // CRDT text
metadata: {
createdAt,
updatedAt,
owner,
permissions: { userId: 'edit' | 'view' | 'comment' }
}
}

// Operations Log (for undo/redo)
[
{ type: 'insert', pos: 10, text: 'Hello', userId: 'u1', timestamp },
{ type: 'delete', pos: 10, length: 5, userId: 'u2', timestamp },
{ type: 'format', range: [10, 15], style: 'bold', userId: 'u1' }
]

// Comments
{
id: 'c1',
documentId: 'doc123',
range: { start: 100, end: 150 },
thread: [
{ userId: 'u1', text: 'Should we rephrase this?', createdAt }
]
}

Interface​

// WebSocket Messages
{
type: 'update',
documentId: 'doc123',
operations: [...], // CRDT operations
userId: 'u1',
cursor: { pos: 123 }
}

// REST API
GET /api/documents/:id
POST /api/documents/:id/comments
GET /api/documents/:id/revisions
POST /api/documents/:id/restore/:revisionId

Optimizations​

1. Operational Transforms / CRDT

// Yjs automatically handles conflicts
ytext.observe(event => {
// Render changes
event.changes.delta.forEach(change => {
if (change.insert) renderInsert(change.insert)
if (change.delete) renderDelete(change.delete)
if (change.retain) applyFormat(change.attributes)
})
})

2. Cursor Synchronization

// Broadcast cursor position
const debouncedCursorSync = debounce((position) => {
ws.send({
type: 'cursor',
userId,
position,
selection: editor.getSelection()
})
}, 50)

// Render other users' cursors
const renderCursors = (cursors) => {
cursors.forEach(({ userId, position, color }) => {
const cursorElement = createCursor(userId, color)
positionAt(position)
})
}

3. Auto-save

const debouncedSave = useMemo(
() => debounce(async (content) => {
await saveDocument(documentId, content)
setLastSaved(new Date())
}, 2000),
[documentId]
)

useEffect(() => {
debouncedSave(content)
}, [content])

4. Version History

// Snapshot every N operations
let operationCount = 0
const SNAPSHOT_INTERVAL = 100

ytext.observe(() => {
operationCount++
if (operationCount >= SNAPSHOT_INTERVAL) {
saveSnapshot(ydoc.toJSON())
operationCount = 0
}
})

5. Large Document Optimization

// Virtualize document rendering
const VisiblePages = () => {
const [visibleRange, setVisibleRange] = useState([0, 5])

return (
<VirtualScroller onScroll={updateVisibleRange}>
{pages.slice(visibleRange[0], visibleRange[1]).map(page => (
<Page key={page.id} content={page.content} />
))}
</VirtualScroller>
)
}

6. Offline Editing

// Queue operations while offline
const operationQueue = []

ytext.observe(event => {
if (!navigator.onLine) {
operationQueue.push(event.changes)
}
})

// Sync when back online
window.addEventListener('online', () => {
provider.sync(operationQueue)
operationQueue.length = 0
})

7. Conflict Resolution

// Last-write-wins for metadata
if (localVersion > remoteVersion) {
merge(local, remote)
} else {
merge(remote, local)
}

// CRDT for text content (automatic)
// Yjs handles concurrent edits without conflicts

Example 4: Real-time Chat Application​

Requirements​

Functional:

  • 1-on-1 and group chats
  • Send text, images, files
  • Read receipts
  • Typing indicators
  • Message reactions
  • Search messages

Non-Functional:

  • < 200ms message delivery
  • Support 1000+ member groups
  • 99.99% message delivery
  • End-to-end encryption
  • Offline message queue
  • 100k+ concurrent connections

Architecture​

ChatApp
β”œβ”€β”€ Sidebar
β”‚ β”œβ”€β”€ ConversationList (virtualized)
β”‚ └── SearchBar
β”œβ”€β”€ ChatWindow
β”‚ β”œβ”€β”€ ChatHeader
β”‚ β”œβ”€β”€ MessageList (virtualized, reverse)
β”‚ β”‚ └── Message
β”‚ β”‚ β”œβ”€β”€ Avatar
β”‚ β”‚ β”œβ”€β”€ Content (text/image/file)
β”‚ β”‚ β”œβ”€β”€ Reactions
β”‚ β”‚ └── Timestamp
β”‚ β”œβ”€β”€ TypingIndicator
β”‚ └── MessageInput
β”‚ β”œβ”€β”€ TextArea
β”‚ β”œβ”€β”€ EmojiPicker
β”‚ └── AttachmentButton
└── NotificationManager

Data Model​

// Message Structure
{
id: 'msg123',
conversationId: 'conv456',
senderId: 'u1',
content: {
type: 'text' | 'image' | 'file',
text: '...',
mediaUrl: '...',
metadata: { fileName, size, mimeType }
},
reactions: {
'πŸ‘': ['u1', 'u2'],
'❀️': ['u3']
},
status: 'sending' | 'sent' | 'delivered' | 'read',
readBy: ['u1', 'u2'],
createdAt: timestamp,
editedAt: timestamp | null
}

// Conversation
{
id: 'conv456',
type: '1-on-1' | 'group',
participants: ['u1', 'u2', 'u3'],
lastMessage: { id, preview, timestamp },
unreadCount: { u1: 5, u2: 0 },
metadata: {
name: 'Project Team',
avatar: 'group.jpg'
}
}

Interface​

// WebSocket Events
ws.on('message:new', (message) => {
appendMessage(message)
playNotificationSound()
})

ws.on('message:read', ({ messageId, userId }) => {
updateReadStatus(messageId, userId)
})

ws.on('typing:start', ({ conversationId, userId }) => {
showTypingIndicator(userId)
})

// REST API (for history)
GET /api/conversations/:id/messages?before=timestamp&limit=50
POST /api/messages
PUT /api/messages/:id/react

Optimizations​

1. Message Virtualization

// Reverse infinite scroll (load older messages)
const MessageList = () => {
const {
data,
fetchPreviousPage,
hasPreviousPage
} = useInfiniteQuery(
['messages', conversationId],
({ pageParam }) => fetchMessages(conversationId, pageParam),
{
getNextPageParam: (lastPage) => lastPage.olderCursor,
select: data => ({
pages: [...data.pages].reverse(),
pageParams: [...data.pageParams].reverse()
})
}
)

return (
<VirtualScroller
reverse={true}
onStartReached={fetchPreviousPage}
>
{messages.map(msg => <Message key={msg.id} {...msg} />)}
</VirtualScroller>
)
}

2. Optimistic Send

const sendMessage = useMutation(postMessage, {
onMutate: async (newMessage) => {
const tempId = generateTempId()
const optimisticMessage = {
...newMessage,
id: tempId,
status: 'sending',
createdAt: Date.now()
}

queryClient.setQueryData(['messages', conversationId], old => ({
...old,
pages: old.pages.map((page, i) =>
i === 0 ? { ...page, messages: [optimisticMessage, ...page.messages] } : page
)
}))

return { tempId }
},
onSuccess: (response, variables, context) => {
// Replace temp message with real one
queryClient.setQueryData(['messages', conversationId], old =>
replaceMessage(old, context.tempId, response.message)
)
},
onError: (err, variables, context) => {
// Mark message as failed
updateMessageStatus(context.tempId, 'failed')
}
})

3. Typing Indicators

const debouncedStopTyping = useMemo(
() => debounce(() => {
ws.send({ type: 'typing:stop', conversationId })
}, 3000),
[conversationId]
)

const handleTyping = () => {
if (!isTyping) {
ws.send({ type: 'typing:start', conversationId, userId })
setIsTyping(true)
}
debouncedStopTyping()
}

4. Read Receipts

// Send read receipt when message enters viewport
const { ref, inView } = useInView({ threshold: 1.0, triggerOnce: true })

useEffect(() => {
if (inView && message.status !== 'read') {
ws.send({
type: 'message:read',
messageId: message.id,
conversationId,
userId
})
}
}, [inView])

5. Image/File Uploads

const uploadFile = async (file) => {
// Generate thumbnail for images
const thumbnail = await generateThumbnail(file)

// Show optimistic UI
const tempMessage = {
id: generateTempId(),
content: { type: 'image', thumbnail },
status: 'uploading',
progress: 0
}
addOptimisticMessage(tempMessage)

// Upload with progress
const formData = new FormData()
formData.append('file', file)

const response = await fetch('/api/upload', {
method: 'POST',
body: formData,
onUploadProgress: (event) => {
updateProgress(tempMessage.id, event.loaded / event.total)
}
})

// Send message with uploaded file URL
const { url } = await response.json()
await sendMessage({
conversationId,
content: { type: 'image', url }
})
}

6. Offline Queue

const messageQueue = []

const sendWithQueue = async (message) => {
if (!navigator.onLine) {
messageQueue.push(message)
showOfflineIndicator()
return
}

try {
await sendMessage(message)
} catch (error) {
if (error.code === 'NETWORK_ERROR') {
messageQueue.push(message)
}
}
}

// Process queue when back online
window.addEventListener('online', async () => {
for (const message of messageQueue) {
await sendMessage(message)
}
messageQueue.length = 0
})

7. Connection Management

const useWebSocket = (conversationId) => {
const ws = useRef(null)
const reconnectAttempts = useRef(0)

useEffect(() => {
const connect = () => {
ws.current = new WebSocket(`wss://chat.example.com?conv=${conversationId}`)

ws.current.onopen = () => {
reconnectAttempts.current = 0
setConnectionStatus('connected')
}

ws.current.onclose = () => {
setConnectionStatus('disconnected')

// Exponential backoff reconnect
const delay = Math.min(1000 * 2 ** reconnectAttempts.current, 30000)
setTimeout(connect, delay)
reconnectAttempts.current++
}

ws.current.onerror = (error) => {
console.error('WebSocket error:', error)
}
}

connect()

return () => ws.current?.close()
}, [conversationId])

return ws.current
}

8. Notification Strategy

// Request permission
Notification.requestPermission()

// Show notification for background messages
ws.on('message:new', (message) => {
if (document.hidden && message.senderId !== currentUserId) {
new Notification(message.senderName, {
body: message.content.text,
icon: message.senderAvatar,
tag: message.conversationId, // Replace previous notification
data: { conversationId: message.conversationId }
})
}
})

// Handle notification click
navigator.serviceWorker.addEventListener('notificationclick', (event) => {
event.notification.close()
clients.openWindow(`/chat/${event.notification.data.conversationId}`)
})

Example 5: Admin Dashboard with Real-time Analytics​

Requirements​

Functional:

  • Real-time metrics (users online, revenue, errors)
  • Multiple chart types (line, bar, pie)
  • Date range filtering
  • Export data (CSV, PDF)
  • Role-based access control
  • Customizable widgets

Non-Functional:

  • < 1s chart render time
  • Handle 1M+ data points
  • 60fps smooth animations
  • Real-time updates every 5s
  • Support 1000+ concurrent admins

Architecture​

Dashboard
β”œβ”€β”€ Header
β”‚ β”œβ”€β”€ DateRangePicker
β”‚ β”œβ”€β”€ RefreshButton
β”‚ └── ExportButton
β”œβ”€β”€ WidgetGrid (drag-and-drop)
β”‚ β”œβ”€β”€ MetricCard (users online, revenue, orders)
β”‚ β”œβ”€β”€ LineChart (sales over time)
β”‚ β”œβ”€β”€ BarChart (top products)
β”‚ β”œβ”€β”€ PieChart (traffic sources)
β”‚ └── DataTable (recent transactions)
└── Sidebar
β”œβ”€β”€ WidgetLibrary
└── Settings

Data Model​

// Dashboard Configuration
{
id: 'dash1',
userId: 'admin1',
layout: [
{ id: 'w1', type: 'metric', position: { x: 0, y: 0, w: 3, h: 2 } },
{ id: 'w2', type: 'lineChart', position: { x: 3, y: 0, w: 9, h: 4 } }
],
filters: {
dateRange: { start: '2024-01-01', end: '2024-01-31' },
region: 'US'
}
}

// Time-series Data
{
metric: 'revenue',
interval: 'hour',
data: [
{ timestamp: 1704067200000, value: 12500 },
{ timestamp: 1704070800000, value: 13200 }
]
}

// Aggregated Metrics
{
usersOnline: 1234,
todayRevenue: 45000,
ordersCount: 567,
errorRate: 0.02,
trend: '+12.5%' // vs yesterday
}

Interface​

// WebSocket (real-time metrics)
ws.on('metrics:update', (data) => {
// { usersOnline: 1250, timestamp: ... }
updateMetrics(data)
})

// REST API (historical data)
GET /api/analytics/timeseries?
metric=revenue&
start=2024-01-01&
end=2024-01-31&
interval=day

GET /api/analytics/top-products?limit=10

POST /api/dashboards/:id/export?format=csv

Optimizations​

1. Data Aggregation

// Client-side aggregation for zoomed-out views
const aggregateData = (data, bucketSize) => {
const buckets = {}

data.forEach(point => {
const bucket = Math.floor(point.timestamp / bucketSize) * bucketSize
if (!buckets[bucket]) {
buckets[bucket] = { sum: 0, count: 0, max: -Infinity, min: Infinity }
}
buckets[bucket].sum += point.value
buckets[bucket].count++
buckets[bucket].max = Math.max(buckets[bucket].max, point.value)
buckets[bucket].min = Math.min(buckets[bucket].min, point.value)
})

return Object.entries(buckets).map(([timestamp, stats]) => ({
timestamp: parseInt(timestamp),
value: stats.sum / stats.count,
max: stats.max,
min: stats.min
}))
}

// Adaptive granularity based on zoom level
const getInterval = (dateRange) => {
const days = (dateRange.end - dateRange.start) / (1000 * 60 * 60 * 24)
if (days <= 1) return 'hour'
if (days <= 7) return 'day'
if (days <= 90) return 'week'
return 'month'
}

2. Chart Performance

// Use canvas for large datasets (>1000 points)
import { Line } from 'react-chartjs-2'

const RevenueChart = ({ data }) => {
const chartData = useMemo(() => ({
labels: data.map(d => d.timestamp),
datasets: [{
label: 'Revenue',
data: data.map(d => d.value),
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
}), [data])

const options = useMemo(() => ({
responsive: true,
animation: data.length > 1000 ? false : true, // Disable animation for large datasets
parsing: false, // Performance optimization
normalized: true,
plugins: {
decimation: {
enabled: data.length > 1000,
algorithm: 'lttb' // Largest Triangle Three Buckets
}
}
}), [data.length])

return <Line data={chartData} options={options} />
}

3. Widget Virtualization

// Only render visible widgets in grid
const WidgetGrid = ({ widgets, layout }) => {
const [visibleWidgets, setVisibleWidgets] = useState(new Set())

const observer = useMemo(
() => new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
setVisibleWidgets(prev => new Set([...prev, entry.target.dataset.widgetId]))
}
})
},
{ rootMargin: '100px' }
),
[]
)

return (
<GridLayout layout={layout}>
{widgets.map(widget => (
<div key={widget.id} data-widget-id={widget.id} ref={el => el && observer.observe(el)}>
{visibleWidgets.has(widget.id) ? (
<Widget {...widget} />
) : (
<WidgetSkeleton />
)}
</div>
))}
</GridLayout>
)
}

4. Real-time Updates with Throttling

// Batch updates to avoid excessive re-renders
let updateBuffer = {}
let updateTimer = null

ws.on('metrics:update', (data) => {
updateBuffer = { ...updateBuffer, ...data }

if (!updateTimer) {
updateTimer = setTimeout(() => {
setMetrics(prev => ({ ...prev, ...updateBuffer }))
updateBuffer = {}
updateTimer = null
}, 1000) // Batch updates every 1 second
}
})

5. Export Functionality

const exportToCSV = (data) => {
const csv = [
['Date', 'Metric', 'Value'],
...data.map(row => [row.date, row.metric, row.value])
].map(row => row.join(',')).join('\n')

const blob = new Blob([csv], { type: 'text/csv' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = `analytics-${Date.now()}.csv`
link.click()
URL.revokeObjectURL(url)
}

const exportToPDF = async (dashboardRef) => {
const canvas = await html2canvas(dashboardRef.current)
const imgData = canvas.toDataURL('image/png')

const pdf = new jsPDF('l', 'mm', 'a4')
const imgWidth = 297
const imgHeight = (canvas.height * imgWidth) / canvas.width

pdf.addImage(imgData, 'PNG', 0, 0, imgWidth, imgHeight)
pdf.save(`dashboard-${Date.now()}.pdf`)
}

6. Caching Strategy

// Cache historical data aggressively
useQuery(
['analytics', metric, dateRange],
() => fetchAnalytics(metric, dateRange),
{
staleTime: 5 * 60 * 1000, // 5 minutes
cacheTime: 30 * 60 * 1000, // 30 minutes
refetchOnWindowFocus: false,
// Prefetch adjacent date ranges
onSuccess: (data) => {
prefetchAdjacentRanges(dateRange)
}
}
)

// Real-time data always fresh
useQuery(
['metrics', 'realtime'],
fetchRealtimeMetrics,
{
refetchInterval: 5000, // 5 seconds
staleTime: 0
}
)

7. Role-based Widget Visibility

const getVisibleWidgets = (userRole, allWidgets) => {
const rolePermissions = {
admin: ['*'],
manager: ['revenue', 'orders', 'customers'],
analyst: ['traffic', 'conversions', 'engagement']
}

const allowed = rolePermissions[userRole] || []

return allWidgets.filter(widget =>
allowed.includes('*') || allowed.includes(widget.type)
)
}

13. Practice Questions​

Here are 10 comprehensive frontend system design questions with key focus areas:

1. Design YouTube Video Player​

Focus Areas:

  • Adaptive bitrate streaming
  • Video buffering strategy
  • Playback controls
  • Captions/subtitles
  • Picture-in-picture
  • Analytics tracking

2. Design Netflix Homepage​

Focus Areas:

  • Horizontal scrolling rows
  • Image lazy loading
  • Video preview on hover
  • Personalized recommendations
  • Infinite scroll
  • Performance at scale

3. Design Twitter Timeline​

Focus Areas:

  • Infinite scroll
  • Real-time tweet updates
  • Media (image/video) optimization
  • Engagement actions (like, retweet)
  • Thread rendering
  • Pagination strategy

4. Design Figma (Collaborative Design Tool)​

Focus Areas:

  • Canvas rendering (WebGL)
  • Real-time collaboration
  • Layer management
  • Undo/redo system
  • Export functionality
  • Performance with complex designs

5. Design Zoom Web Client​

Focus Areas:

  • WebRTC video/audio
  • Screen sharing
  • Chat (real-time)
  • Participant management
  • Network resilience
  • Bandwidth optimization

6. Design Dropbox File Browser​

Focus Areas:

  • File tree navigation
  • Upload/download queue
  • File preview
  • Search and filtering
  • Offline sync
  • Conflict resolution

7. Design Trello Board​

Focus Areas:

  • Drag-and-drop
  • Real-time updates
  • Card management
  • List virtualization
  • Undo/redo
  • Offline support

8. Design Spotify Web Player​

Focus Areas:

  • Audio streaming
  • Playlist management
  • Search with filters
  • Queue management
  • Offline playback
  • Cross-device sync

9. Design LinkedIn Feed​

Focus Areas:

  • Infinite scroll
  • Post types (text, image, video, poll)
  • Engagement tracking
  • Real-time updates
  • Sponsored content
  • Content moderation

10. Design Notion Editor​

Focus Areas:

  • Block-based editor
  • Real-time collaboration
  • Drag-and-drop blocks
  • Nested structure
  • Export formats
  • Performance with large documents

14. Interview Preparation Checklist​

Week 1-2: Foundations​

  • Review JavaScript event loop, closures, prototypes
  • Understand browser rendering pipeline
  • Deep dive into React reconciliation and fiber
  • Study state management patterns (Context, Redux, React Query)
  • Practice drawing component hierarchies

Week 3: Architecture Patterns​

  • Understand CSR vs SSR vs SSG trade-offs
  • Study micro frontends (build-time vs runtime)
  • Learn monorepo strategies (Nx, Turborepo)
  • Practice designing high-level architecture diagrams

Week 4: Performance​

  • Master code splitting techniques
  • Understand Core Web Vitals (FCP, LCP, CLS, TTI)
  • Study virtualization libraries
  • Learn image optimization strategies
  • Practice identifying performance bottlenecks

Week 5: Real-time & Advanced​

  • Understand WebSockets vs SSE vs Polling
  • Study CRDT for collaborative editing
  • Learn offline-first architecture
  • Understand service workers and caching strategies

Week 6: Mock Interviews​

  • Practice 2 system design questions daily
  • Time yourself (45 minutes per question)
  • Record yourself explaining designs
  • Get feedback from peers or mentors
  • Review and refine your approach

15. Common Interview Mistakes to Avoid​

1. Jumping to Implementation Too Quickly​

Mistake: Start coding without understanding requirements Fix: Spend first 5-10 minutes clarifying requirements

2. Ignoring Non-Functional Requirements​

Mistake: Focus only on features Fix: Always ask about scale, performance, security

3. Over-engineering​

Mistake: Suggesting micro frontends for a small app Fix: Choose simplest solution that meets requirements

4. Under-engineering​

Mistake: Not considering scalability for large apps Fix: Think about team size, future growth

5. Not Discussing Trade-offs​

Mistake: Presenting one solution without alternatives Fix: Always discuss pros/cons of different approaches

6. Forgetting Performance​

Mistake: No mention of metrics, optimization Fix: Always include performance section

7. Skipping Security​

Mistake: Not discussing XSS, CSRF, auth Fix: Always have a security subsection

8. Poor Communication​

Mistake: Drawing silently, not explaining thinking Fix: Think out loud, involve interviewer

9. Ignoring Accessibility​

Mistake: Building UI without a11y considerations Fix: Mention WCAG, keyboard nav, screen readers

10. Not Asking Questions​

Mistake: Making assumptions Fix: Clarify ambiguities early


16. Evaluation Rubric (What Interviewers Look For)​

Requirements Gathering (20%)​

  • Asks clarifying questions
  • Identifies functional vs non-functional requirements
  • Understands user personas and use cases
  • Defines success metrics

Architecture Design (25%)​

  • Proposes appropriate high-level architecture
  • Justifies rendering strategy (CSR/SSR/SSG)
  • Designs clear component hierarchy
  • Considers team scalability

Data Modeling (15%)​

  • Defines clear data structures
  • Separates UI state from server state
  • Chooses appropriate pagination strategy
  • Plans caching approach

API Design (10%)​

  • Defines clean API contracts
  • Considers error handling
  • Plans for real-time updates if needed
  • Thinks about versioning

Performance (15%)​

  • Identifies bottlenecks
  • Proposes concrete optimizations
  • Mentions specific metrics (FCP, LCP, TTI)
  • Considers bundle size and code splitting

Scalability (10%)​

  • Discusses horizontal scaling
  • Plans for growing data
  • Considers CDN and caching
  • Thinks about monitoring

Communication (5%)​

  • Explains clearly
  • Draws diagrams
  • Involves interviewer
  • Manages time well

17. Quick Reference: Technology Choices​

State Management​

ToolBest ForAvoid For
useStateLocal component stateShared state
ContextTheme, auth, i18nFrequently changing data
ReduxComplex state logicSimple CRUD
ZustandLightweight global stateServer data
JotaiAtomic stateLarge state trees
React QueryServer/async stateLocal UI state

Rendering Strategy​

StrategyBest ForAvoid For
CSRWeb apps, authenticatedSEO-critical, slow networks
SSRE-commerce, content sitesHighly interactive apps
SSGBlogs, docs, marketingDynamic, personalized content
ISRSemi-static contentReal-time data
IslandsContent + widgetsFully interactive apps

Real-time Communication​

TechnologyBest ForAvoid For
WebSocketBi-directional, chat, gamingSimple notifications
SSEServer→client updatesBi-directional needs
PollingLegacy support, simple updatesHigh-frequency updates
WebRTCVideo/audioText messages

Data Fetching​

PatternBest ForAvoid For
fetch on mountInitial dataAvoiding waterfalls
Parallel fetchingIndependent requestsSequential dependencies
WaterfallSequential dependenciesPerformance-critical
PrefetchingPredictable navigationUnpredictable behavior
Optimistic updatesBetter UXComplex rollback logic

18. System Design Interview Template​

Use this template for any frontend system design interview:

1. Clarify Requirements (5-10 min)​

Functional:
- [ ] Core features?
- [ ] User personas?
- [ ] Critical user flows?

Non-Functional:
- [ ] Scale (users, data)?
- [ ] Performance targets?
- [ ] Browser/device support?
- [ ] Security requirements?

2. High-Level Architecture (10-15 min)​

- Rendering strategy (CSR/SSR/SSG)
- Component hierarchy
- State management approach
- API architecture (REST/GraphQL/WebSocket)

3. Deep Dives (20-25 min)​

Pick 2-3 areas based on requirements:

Data Model

  • Entity relationships
  • Normalization strategy
  • Caching approach

Performance

  • Code splitting points
  • Image optimization
  • Virtualization needs
  • Key metrics

Real-time

  • Communication protocol
  • Conflict resolution
  • Offline support

Scalability

  • CDN strategy
  • Micro frontends (if applicable)
  • Monitoring plan

4. Trade-offs & Alternatives (5 min)​

  • Discuss pros/cons of chosen approach
  • Mention alternative solutions
  • Explain why you chose this path

19. Resources for Further Learning​

Books​

  • "Designing Data-Intensive Applications" by Martin Kleppmann
  • "High Performance Browser Networking" by Ilya Grigorik
  • "Learning JavaScript Design Patterns" by Addy Osmani

Online Courses​

  • Frontend Masters: "Frontend System Design"
  • GreatFrontEnd: System Design Guide
  • ByteByteGo: System Design for Beginners

Practice Platforms​

  • GreatFrontEnd (system design questions)
  • Frontend Mentor (implementation practice)
  • LeetCode (data structures & algorithms)

Articles & Blogs​

  • web.dev (performance, best practices)
  • React.dev (React patterns)
  • Patterns.dev (design patterns)
  • Martin Fowler's blog (architecture)

YouTube Channels​

  • ByteByteGo
  • System Design Interview
  • Web Dev Simplified
  • Kent C. Dodds

20. Final Tips for Success​

Before the Interview​

  1. Prepare a mental checklist using RADIO framework
  2. Practice drawing architecture diagrams quickly
  3. Time yourself - 45 minutes is standard
  4. Record mock interviews to review communication

During the Interview​

  1. Think out loud - silence is bad
  2. Ask questions - show you're thorough
  3. Use the whiteboard - visual > verbal
  4. Manage time - don't spend 30min on requirements
  5. Be flexible - adapt based on interviewer feedback

After the Interview​

  1. Take notes on questions asked
  2. Reflect on what went well/poorly
  3. Research any gaps in knowledge
  4. Practice the same question again

Summary​

Frontend system design is about:

  • Understanding requirements deeply
  • Making informed trade-offs between competing concerns
  • Thinking at scale - users, data, teams, time
  • Communicating clearly your design decisions
  • Balancing user experience, performance, maintainability

Success comes from:

  • βœ… Solid fundamentals (JS, browser, React)
  • βœ… Pattern recognition (seen similar problems)
  • βœ… Systematic approach (like RADIO)
  • βœ… Practice, practice, practice

Good luck with your interviews! πŸš€


Appendix: Common Acronyms​

  • SPA: Single Page Application
  • SSR: Server-Side Rendering
  • SSG: Static Site Generation
  • CSR: Client-Side Rendering
  • ISR: Incremental Static Regeneration
  • FCP: First Contentful Paint
  • LCP: Largest Contentful Paint
  • TTI: Time to Interactive
  • CLS: Cumulative Layout Shift
  • FID: First Input Delay
  • TBT: Total Blocking Time
  • CRDT: Conflict-free Replicated Data Type
  • OT: Operational Transform
  • BFF: Backend For Frontend
  • CDN: Content Delivery Network
  • CORS: Cross-Origin Resource Sharing
  • CSP: Content Security Policy
  • XSS: Cross-Site Scripting
  • CSRF: Cross-Site Request Forgery
  • JWT: JSON Web Token
  • WCAG: Web Content Accessibility Guidelines